﻿import com.kirillam.handy.core.HATControlClip;
import com.kirillam.handy.datastructures.WeakReference;
import com.kirillam.handy.events.HATEvent;
import com.kirillam.handy.repeaters.HATFrameTimer;


/**
* SProvides a way to execute a method of an object as a job.
* 
* @author Kirill Mourzenko
*/

class com.kirillam.handy.repeaters.HATJob extends HATFrameTimer
{
	private var target	: WeakReference;
	
	/**
	 * Constructor
	 */
	
	public function HATJob() 
	{
		super();
	}
	
	
	/**
	 * Calls the method repeatedly once every given amount of frames.
	 * 
	 * @param	interval	Number of frames between calls.
	 * @param	target		Object that has the method to execute.
	 * @param	method		Method to execute. Must take this object as the first parameter, all other arguments come after it.
	 * 						It can be used to stop the job prematurely within the method.
	 * @param	...			Any extra arguments to pass through to the method.
	 */
	
	public function repeat( interval : Number, target : Object, method : Function ) : HATJob
	{
		repeatNTimesArgs( null, interval, target, method, arguments.slice( 3 ) );
		return this;
	}
	
	
	/**
	 * Calls the method repeatedly once every given amount of frames N times.
	 * 
	 * @param	count		Number of times to call the function.
	 * @param	interval	Number of frames between calls.
	 * @param	target		Object that has the method to execute.
	 * @param	method		Method to execute. Must take this object as the first parameter, all other arguments come after it.
	 * 						It can be used to stop the job prematurely within the method.
	 * @param	...			Any extra arguments to pass through to the method.
	 */
	
	public function repeatNTimes( count : Number, interval : Number, target : Object, method : Function ) : HATJob
	{
		repeatNTimesArgs( count, interval, target, method, arguments.slice( 4 ) );
		return this;
	}
	
	
	/**
	 * Calls the method immediately and then once every given amount of frames.
	 * 
	 * @param	interval	Number of frames between calls.
	 * @param	target		Object that has the method to execute.
	 * @param	method		Method to execute. Must take this object as the first parameter, all other arguments come after it.
	 * 						It can be used to stop the job prematurely within the method.
	 * @param	...			Any extra arguments to pass through to the method.
	 */
	
	public function callAndRepeat( interval : Number, target : Object, method : Function ) : HATJob
	{
		repeatNTimesArgs( null, interval, target, method, arguments.slice( 3 ) );
		method.apply( target, arguments.slice( 3 ) );
		return this;
	}
	
	
	/**
	 * Calls the method N Times immediately and then once every given amount of frames.
	 * 
	 * @param	count		Number of times to call the method.
	 * @param	interval	Number of frames between calls.
	 * @param	target		Object that has the method to execute.
	 * @param	method		Method to execute. Must take this object as the first parameter, all other arguments come after it.
	 * 						It can be used to stop the job prematurely within the method.
	 * @param	...			Any extra arguments to pass through to the method.
	 */
	
	public function callAndRepeatNTimes( count : Number, interval : Number, target : Object, method : Function ) : HATJob
	{
		repeatNTimesArgs( count - 1, interval, target, method, arguments.slice( 4 ) );
		method.apply( target, arguments.slice( 4 ) );
		return this;
	}
	
	
	/**
	 * Calls the method after a certain amout of frames.
	 * 
	 * @param	interval	Number of frames to wait.
	 * @param	target		Object that has the method to execute.
	 * @param	method		Method to execute. Must take this object as the first parameter, all other arguments come after it.
	 * 						It can be used to stop the job prematurely within the method.
	 * @param	...			Any extra arguments to pass through to the method.
	 */
	
	public function callLater( interval : Number, target : Object, method : Function ) : HATJob
	{
		repeatNTimesArgs( 1, interval, target, method, arguments.slice( 3 ) );
		return this;
	}
	
	
	/** Stops the method from executing at later times. */
	
	public function quit()
	{
		ignore( HATEvent.PULSE, this );
		
		delete target.object.__HAT_JOBS[ hatId() ];
		target	= null;
		
		kill();
	}
	
	
	/*
	 * Calls the method repeatedly once every given amount of frames N times.
	 * 
	 * @param	count		Number of times to call the method.
	 * @param	interval	Number of frames between calls.
	 * @param	target		Object that has the method to execute.
	 * @param	method		Method to execute. Must take this object as the first parameter, all other arguments come after it.
	 * 						It can be used to stop the job prematurely within the method.
	 * @param	args		Array of any extra arguments to pass through to the method.
	 */
	
	private function repeatNTimesArgs( count : Number, interval : Number, target : Object, method : Function, args : Array )
	{
		quit();
		
		this.target	= new WeakReference( target );
		
		if( target.__HAT_JOBS == null ) {
			target.__HAT_JOBS	= new Object();
			_global.ASSetPropFlags( target, "__HAT_JOBS", 7, true );
		}
		

		if( target.__HAT_JOBS[ hatId() ] == null ) {
			target.__HAT_JOBS[ hatId() ] = [ method, args ];
		}
		
		handle( HATEvent.PULSE, this, onRunJob, true );
		start( interval, count );
	}
	
	
	/* Handles HATEvent.PULSE event and executes the job method. */
	
	private function onRunJob( e : HATEvent )
	{
		var target	: Object	= this.target.object;
		
		if( target != null ) {
			
			var job	: Array		= target.__HAT_JOBS[ hatId() ];
			job[ 0 ].apply( target, [ this ].concat( job[ 1 ] ) );
		} else {
			quit();
		}
	}
	
	
	/** 
	 * Converts the job to its string representation.
	 * 
	 * @return The job's string representation.
	 */
	
	public function toString() : String
	{
		return String( hatId() );
	}
}